/* 
 * File:   Instance.cpp
 * Author: brady
 * 
 * Created on September 6, 2011, 5:50 PM
 */

#include <shapes/Instance.h>

Instance::Instance() {
    this->shape = NULL;
    this->shader = NULL;
    TMatrix.setIdentity();
    TMatrix_inv.setIdentity();
}

Instance::Instance(const Instance& orig) {
}

Instance::Instance(Shape* shape) {
    this->shape = shape;
    TMatrix.setIdentity();
    TMatrix_inv.setIdentity();
    this->shader = shape->getShader();
}

Instance::~Instance() {
}

void Instance::rotate(double degrees, int axis) {
    TMatrix.rotate(degrees, axis);
}

void Instance::scale(double sx, double sy, double sz) {
    TMatrix.scale(sx, sy, sz);
}

void Instance::translate(double dx, double dy, double dz) {
    TMatrix.translate(dx, dy, dz);
}

void Instance::finalize() {
    this->TMatrix_inv = TMatrix.getInverse();
    buildBoundingBox();
}

bool Instance::testIntersect(IntersectionParams& ip, IntersectionResult& ir) {
    Vector3D tOrigin = TMatrix_inv.transform(ip.getRay().getOrigin());
    Vector3D tDirection = TMatrix_inv.transform(ip.getRay().getOrigin() + ip.getRay().getDirection());
    tDirection -= tOrigin;
    Ray tRay = Ray(tOrigin, tDirection);
    IntersectionParams ip_t(tRay, ip.getTMin(), ip.getTMax());
    bool hit = shape->testIntersect(ip_t, ir);
    if(hit){
        ir.shape = this;
        ir.normal = TMatrix_inv.transposeTransform(ir.normal).normalize();
        ir.hitPoint = ip.getRay().follow(ir.tVal);
        ir.ray = ip.getRay();
    }
    else{
        ir.shape = NULL;
        ir.tVal = DBL_MAX;
    }
    return hit;
}

void Instance::buildBoundingBox(){
    double xMin = shape->getBoundingBox().getMin()[0];
    double yMin = shape->getBoundingBox().getMin()[1];
    double zMin = shape->getBoundingBox().getMin()[2];
    double xMax = shape->getBoundingBox().getMax()[0];
    double yMax = shape->getBoundingBox().getMax()[1];
    double zMax = shape->getBoundingBox().getMax()[2];
    
    Vector3D points[8];
    Vector3D A, B, C, D, E, F, G, H;
    A = Vector3D(xMin, yMin, zMin);
    B = Vector3D(xMax, yMin, zMin);
    C = Vector3D(xMin, yMin, zMax);
    D = Vector3D(xMax, yMin, zMax);
    E = Vector3D(xMin, yMax, zMin);
    F = Vector3D(xMax, yMax, zMin);
    G = Vector3D(xMin, yMax, zMax);
    H = Vector3D(xMax, yMax, zMax);

    points[0] = A;
    points[1] = B;
    points[2] = C;
    points[3] = D;
    points[4] = E;
    points[5] = F;
    points[6] = G;
    points[7] = H;

    xMin = DBL_MAX;
    yMin = DBL_MAX;
    zMin = DBL_MAX;
    xMax = -DBL_MAX;
    yMax = -DBL_MAX;
    zMax = -DBL_MAX;

    //transform the bounding box
    for(int i = 0; i<8; i++)
    {
        Vector3D pt = points[i];
        pt = TMatrix.transform(pt);
        points[i] = pt;
    }

    //re-axis-align
    for(int i = 0; i<8; i++)
    {
        xMin = min(xMin, points[i][0]);
        xMax = max(xMax, points[i][0]);
        yMin = min(yMin, points[i][1]);
        yMax = max(yMax, points[i][1]);
        zMin = min(zMin, points[i][2]);
        zMax = max(zMax, points[i][2]);
    }

    this->boundingBox = BoundingBox(Vector3D(xMin, yMin, zMin), Vector3D(xMax, yMax, zMax));
}